﻿using System;
using System.Collections.Generic;
using System.Text;
using eslib.CRClib;

namespace eslib.nnp5.layers
{
    /// <summary>
    /// 转义数据包
    /// </summary>
    public class TransferredPKG : TransportPKGBase
    {


        #region 构建包

        /// <summary>
        /// 构造新的FF(不包括MicroFF)数据包，应在创建后立即分配包ID (setPkgID)
        /// </summary>
        public TransferredPKG()
        {
            dataSign = 0;
            pkgID = 0;
            CRC = new byte[4];
            data = new List<byte>();
        }




        /// <summary>
        /// 以数据包缓存还原数据包
        /// </summary>
        /// <param name="pkgBuffer"></param>
        /// <returns></returns>
        public static TransferredPKG CreateTransferredPKG(byte[] pkgBuffer)
        {
            TransferredPKG pkg = new TransferredPKG();

            //数据段标识
            pkg.dataSign = BitConverter.ToUInt32(pkgBuffer, 0);
            int olen = 4;       //数据标识、包ID和crc段长度
            int datalen = pkg.getDataLen();     //数据段长度

            //包ID
            pkg.pkgID = BitConverter.ToUInt32(pkgBuffer, olen);
            olen += 4;

            //CRC段            
            if (pkg.isUseCRC())
            {
                for (int i = 0; i < 4; i++)
                {
                    pkg.CRC[i] = pkgBuffer[i + olen];
                }
                olen += 4;
            }


            //数据段
            if ((olen + datalen) != pkgBuffer.Length)
                throw new TransportLayerException("数据包长度不正确");

            for (int i = 0; i < datalen; i++)
            {
                pkg.data.Add(pkgBuffer[i + olen]);
            }


            //CRC检验
            if (pkg.isUseCRC())
            {
                if (!pkg.crcCheck())
                {
                    throw new TransportLayerException("重组传输层数据包时CRC校验不通过");
                }
            }

            return pkg;
        }




        /// <summary>
        /// 获取完整转义数据包(包括包头包尾)
        /// </summary>
        /// <returns></returns>
        public override byte[] makePKG()
        {
            //构造原始数据
            List<byte> pkgOri = new List<byte>();


            //更新数据段长度
            setDataLen();

            //更新CRC
            if (isUseCRC())
            {
                updateCRC();
            }


            //数据段标识
            pkgOri.AddRange(BitConverter.GetBytes(dataSign));

            //包ID
            pkgOri.AddRange(BitConverter.GetBytes(pkgID));

            //CRC段
            if (isUseCRC())
            {
                pkgOri.AddRange(CRC);
            }

            pkgOri.AddRange(data);     //数据段


            //整体FF转义
            List<byte> pkg = ffTransData(pkgOri);


            //补充包头包尾
            pkg.InsertRange(0, head);
            pkg.AddRange(tail);

            return pkg.ToArray();
        }


        #endregion





        #region 数据结构


        #region 数据段标识

        /// <summary>
        /// 数据段标识
        /// </summary>
        private uint dataSign;

        /// <summary>
        /// 更新数据段长度
        /// </summary>
        private void setDataLen()
        {
            uint len = (uint)data.Count;
            dataSign &= crcSignBit;     //数据段长度清0
            dataSign |= (len & (~crcSignBit));
        }


        /// <summary>
        /// 获取数据段长度
        /// </summary>
        /// <returns></returns>
        private int getDataLen()
        {
            return (int)(dataSign & (~crcSignBit));
        }


        /// <summary>
        /// crc标识位，最高位
        /// </summary>
        private uint crcSignBit = 0x80000000;

        /// <summary>
        /// 获取CRC使用标识（使用CRC返回true)
        /// </summary>
        /// <returns></returns>
        public bool isUseCRC()
        {
            return (dataSign & crcSignBit) > 0;
        }

        /// <summary>
        /// 设置CRC标识
        /// </summary>
        /// <param name="useCRC">使用CRC时为true</param>
        public void setCRCSign(bool useCRC)
        {
            if (useCRC)
            {
                dataSign |= crcSignBit;
            }
            else
            {
                dataSign &= (~crcSignBit);
            }
        }

        #endregion




        #region CRC

        /// <summary>
        /// CRC数据
        /// </summary>
        private byte[] CRC;


        /// <summary>
        /// 检查CRC，正确返回true
        /// </summary>
        /// <returns></returns>
        private bool crcCheck()
        {
            EsCrc escrc = new EsCrc();
            uint dataCRC = escrc.ComputeCrc(GetData());       //数据段crc
            uint originCRC = BitConverter.ToUInt32(this.CRC, 0);        //原始CRC
            return dataCRC == originCRC;
        }

        /// <summary>
        /// 更新CRC
        /// </summary>
        private void updateCRC()
        {
            EsCrc escrc = new EsCrc();
            this.CRC = EsCrc.Uint2Bytes(escrc.ComputeCrc(GetData()));
        }

        #endregion


        #region 数据段

        /// <summary>
        /// 数据段
        /// </summary>
        private List<byte> data;

        /// <summary>
        /// 获取数据
        /// </summary>
        /// <returns></returns>
        public byte[] GetData()
        {
            return data.ToArray();
        }

        /// <summary>
        /// 设置数据段,会先清空原有数据
        /// </summary>
        /// <param name="buf">原始数据</param>
        public void SetData(byte[] buf)
        {
            data = new List<byte>();
            data.AddRange(buf);
        }


        #endregion

        #endregion

    }
}
